﻿using System;
using System.ComponentModel;
using Microsoft.WowAddonStudio.FrameXml.Components.Events;
using Microsoft.WowAddonStudio.FrameXml.Components.Events.ScriptManager;
using Microsoft.WowAddonStudio.FrameXml.Serialization;

namespace Microsoft.WowAddonStudio.FrameXml.Components.Descriptors
{
    public class FrameXmlEventDescriptor : EventDescriptor
    {
        private readonly Type componentType;
        private readonly EventName eventName;

        public FrameXmlEventDescriptor(Type componentType, EventName eventName, Attribute[] attributes)
            : base(eventName.ToString(), attributes)
        {
            if (componentType == null)
                throw new ArgumentNullException("componentType");
            if (!(componentType == typeof(FrameControl) || componentType.IsSubclassOf(typeof(FrameControl))))
                throw new ArgumentException("FrameXML events are only supported on Frame controls.");

            this.componentType = componentType;
            this.eventName = eventName;
        }

        public FrameXmlEventDescriptor(Type componentType, EventName eventName)
            : this(componentType, eventName, null)
        {
        }

        /// <summary>
        /// Gets the name of the category to which the member belongs, as specified in the <see cref="T:System.ComponentModel.CategoryAttribute"/>.
        /// </summary>
        /// <value></value>
        /// <returns>The name of the category to which the member belongs. If there is no <see cref="T:System.ComponentModel.CategoryAttribute"/>, the category name is set to the default category, Misc.</returns>
        /// <PermissionSet>
        /// 	<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
        /// </PermissionSet>
        public override string Category
        {
            get { return "Events"; }
        }

        /// <summary>
        /// When overridden in a derived class, gets the type of component this event is bound to.
        /// </summary>
        /// <value></value>
        /// <returns>
        /// A <see cref="T:System.Type"/> that represents the type of component the event is bound to.
        /// </returns>
        public override Type ComponentType
        {
            get { return componentType; }
        }

        /// <summary>
        /// Gets the name of the event.
        /// </summary>
        /// <value>The name of the event.</value>
        public EventName EventName
        {
            get { return eventName; }
        }

        /// <summary>
        /// When overridden in a derived class, gets the type of delegate for the event.
        /// </summary>
        /// <value></value>
        /// <returns>
        /// A <see cref="T:System.Type"/> that represents the type of delegate for the event.
        /// </returns>
        public override Type EventType
        {
            get { return typeof(FrameXmlEventHandler); }
        }

        /// <summary>
        /// When overridden in a derived class, gets a value indicating whether the event delegate is a multicast delegate.
        /// </summary>
        /// <value></value>
        /// <returns>true if the event delegate is multicast; otherwise, false.
        /// </returns>
        public override bool IsMulticast
        {
            get { return true; }
        }

        /// <summary>
        /// When overridden in a derived class, binds the event to the component.
        /// </summary>
        /// <param name="component">A component that provides events to the delegate.</param>
        /// <param name="value">A delegate that represents the method that handles the event.</param>
        public override void AddEventHandler(object component, Delegate value)
        {
            var script = (string) value.Method.Invoke(value.Target, null);

            ISite site = GetSite(component);

            if (site != null && !string.IsNullOrEmpty(script))
            {
                var scriptManager = (IFrameXmlScriptManager)site.GetService(typeof (IFrameXmlScriptManager));
                if (scriptManager != null)
                    scriptManager.AddScript((FrameControl)component, eventName, script);
            }
        }

        /// <summary>
        /// When overridden in a derived class, unbinds the delegate from the component so that the delegate will no longer receive events from the component.
        /// </summary>
        /// <param name="component">The component that the delegate is bound to.</param>
        /// <param name="value">The delegate to unbind from the component.</param>
        public override void RemoveEventHandler(object component, Delegate value)
        {
            ISite site = GetSite(component);

            if (site != null)
            {
                var scriptManager = (IFrameXmlScriptManager) site.GetService(typeof (IFrameXmlScriptManager));
                if (scriptManager != null)
                    scriptManager.RemoveScript((FrameControl)component, eventName);
            }
        }
    }
}